﻿/********************************************************************************
* Copyright 2010 Zane Thorn (zane.thorn@gmail.com)                              *
*                                                                               *
* NeturalMath is free software: you can redistribute it and/or modify           *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation, either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* NeturalMath is distributed in the hope that it will be useful,                *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
* GNU Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with NeturalMath.  If not, see <http://www.gnu.org/licenses/>.          *
********************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace NeturalMath.Expressions
{
    public abstract class MathExpressionVisitor<T>
    {
        #region Constructors

        protected MathExpressionVisitor(MathRuntime runtime)
        {
            Runtime = runtime;
        }

        #endregion

        #region Public Properties

        public MathRuntime Runtime { get; private set; }

        #endregion

        public T Visit(MathExpression expression)
        {
            if (expression == null)
                return default(T);

            var voidExpression = expression as VoidExpression;
            if (voidExpression != null)
                return VisitVoidExpression(voidExpression);

            var valueExpression = expression as ValueExpression;
            if (valueExpression != null)
                return VisitValueExpression(valueExpression);

            throw new InvalidOperationException();
        }

        private T VisitValueExpression(ValueExpression expression)
        {

            var unary = expression as UnaryOperatorExpression;
            if (unary != null)
                return VisitUnaryOperator(unary);

            var delegateExpression = expression as DelegateExpression;
            if (delegateExpression != null)
                return VisitDelegate(delegateExpression);

            var lookupExpression = expression as LookupExpression;
            if (lookupExpression != null)
                return VisitLookupExpression(lookupExpression);

            var domain = expression as DomainExpression;
            if (domain != null)
                return VisitDomain(domain);

            var constant = expression as ConstantValueExpression;
            if (constant != null)
                return VisitConstant(constant);

            var binary = expression as BinaryOperatorExpression;
            if (binary != null)
                return VisitBinaryOperator(binary);

            throw new InvalidOperationException();
        }

        private T VisitVoidExpression(VoidExpression expression)
        {
            var assignment = expression as AssignmentExpression;
            if (assignment != null)
                return VisitAssignment(assignment);

            var keyword = expression as KeywordExpression;
            if (keyword != null)
                return VisitKeywordExpression(keyword);

            var parameter = expression as FunctionParameterExpression;
            if (parameter != null)
                return VisitParameterExpression(parameter);

            throw new InvalidOperationException();
        }

        private T VisitKeywordExpression(KeywordExpression expression)
        {

            var import = expression as ImportKeywordExpression;
            if (import != null)
                return VisitImportKeyword(import);

            var print = expression as PrintKeywordExpression;
            if (print != null)
                return VisitPrintKeyword(print);

            var def = expression as DefKeywordExpression;
            if (def != null)
                return VisitDefKeyword(def);

            throw new InvalidOperationException();
        }

        private T VisitLookupExpression(LookupExpression expression)
        {
            var symbolic = expression as SymbolicLookupExpression;
            if (symbolic != null)
                return VisitSymbolicLookup(symbolic);

            var functionCall = expression as FunctionUseExpression;
            if (functionCall != null)
                return VisitFunctionCall(functionCall);

            return VisitMemberLookup(expression);
        }

        #region Abstract Methods

        // voids
        protected abstract T VisitParameterExpression(FunctionParameterExpression expression);
        protected abstract T VisitAssignment(AssignmentExpression expression);
        // Keywords
        protected abstract T VisitImportKeyword(ImportKeywordExpression expression);
        protected abstract T VisitDefKeyword(DefKeywordExpression expression);
        protected abstract T VisitPrintKeyword(PrintKeywordExpression expression);

        // values
        protected abstract T VisitBinaryOperator(BinaryOperatorExpression expression);
        protected abstract T VisitUnaryOperator(UnaryOperatorExpression expression);
        protected abstract T VisitConstant(ConstantValueExpression expression);
        protected abstract T VisitDelegate(DelegateExpression expression);
        protected abstract T VisitDomain(DomainExpression expression);
        // lookups
        protected abstract T VisitMemberLookup(LookupExpression expression);
        protected abstract T VisitSymbolicLookup(SymbolicLookupExpression expression);
        protected abstract T VisitFunctionCall(FunctionUseExpression expression);
        

        

        #endregion
    }
}
